package com.iussoft.gateway.protocol;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.apache.database.constant.SpringContextLoader;
import com.apache.method.SystemRunnable;
import com.apache.rpc.common.XMLMapParse;
import com.apache.tools.ConfigUtil;
import com.apache.tools.DateUtils;
import com.apache.tools.StrUtil;
import com.apache.tools.UUIDUtil;
import com.iussoft.gateway.filter.Response;
import com.iussoft.gateway.routes.GatewayDefineService;
import com.iussoft.manager.RedisManager;
import com.iussoft.tools.HttpService;
import com.iussoft.tools.JedisPoolFactory;
import com.iussoft.tools.LoggerUtil;
import com.iussoft.tools.XMLUtil;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.DiscoveryManager;
import com.netflix.discovery.shared.Application;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;

/**
 * description: 启动socke端口的监听
 *
 * @author dyh 创建时间：2018年01月22日
 */
public class GatewaySocketListener implements ApplicationListener<ContextRefreshedEvent> {

    private Logger log = Logger.getLogger("clientSocket");

    private String port;

    private static boolean isStart = false;

    private GatewaySocketListener() {

    }

    public GatewaySocketListener(String port) {
        this.port = port;
    }

    public void init() {
        //init
    }

    public void registry() {
        new Thread(new ProtocolServer(port), "socket 服务线程").start();
    }

    public void setPort(String port) {
        this.port = port;
    }

    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isStart)
            return;
        //当ApplicationContext初始化或者刷新时触发该事件
        if (event.getTimestamp() > 0) {
            registry();
        }
        isStart = true;
    }

    class ProtocolServer extends SystemRunnable {

        private ServerSocket server = null;

        private String portStr;

        public ProtocolServer(String portStr) {
            this.portStr = portStr;
        }

        public void run() {
            //是否开发socket监听，默认开启
            String openServer = StrUtil
                    .doNull(ConfigUtil.getInstance().getValueByKey("openSocketServer"), "1");
            if ("1".equals(openServer)) {
                portStr = StrUtil.doNull(portStr, "2818");
                try {
                    if (null != server) {
                        return;
                    }
                    server = new ServerSocket(Integer.parseInt(portStr));
                    isStart = true;
                } catch (Exception e1) {
                    e1.printStackTrace();
                    closeSocket(portStr);
                }
                while ("1".equals(openServer)) {
                    try {
                        Socket socket = server.accept();
                        if (null != socket) {
                            new Thread(new SocketServiceTimer(socket), "socket 服务线程").start();
                        }
                    } catch (IOException e) {
                        log.error("获取可能socke连接出错：" + e.getMessage());
                    }
                }
            }
        }

        private void closeSocket(String port) {
            try {
                if (!server.isClosed()) {
                    server.close();
                    server = null;
                    if (!StrUtil.isNull(port))
                        server = new ServerSocket(Integer.parseInt(port));
                }
            } catch (Exception e) {
                log.error("创建socket server 出错：" + e.getMessage());
            }
        }

        private class SocketServiceTimer implements Runnable {

            private Socket socket = null;

            public SocketServiceTimer(Socket socket) {
                this.socket = socket;
            }

            public void run() {
                String result = "";
                String key = "";
                if (null != socket) {
                    key = socket.getInetAddress().getHostAddress();
                }
                BufferedReader bufferedReader = null;
                try {
                    String strMsg = "";
                    if (socket != null && socket.isConnected() && Thread.currentThread().isAlive()
                            && !Thread.currentThread().isInterrupted() && !socket.isClosed()) {
                        //byte[] data = new byte[102400];
                        String mg = null;
                        try {
                            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                            StringBuilder sb = new StringBuilder("");
                            while ((mg = bufferedReader.readLine()) != null) {
                                sb.append(mg);
                            }
                            strMsg = sb.toString();
                        }catch (IOException e){
                            return;
                        }
                        if (StrUtil.isNull(strMsg)) {
                            return;
                        }
                        strMsg = strMsg.trim();
                        DiscoveryManager discoveryClient = DiscoveryManager.getInstance();
                        String traceId = UUIDUtil.getUUID("",32);
                        String format = XMLUtil.isXmlDocument(strMsg) ? "xml" : "json";
                        String interfaceCode = "";
                        String params = null;
                        if("json".equals(format)) {//解板json格式的{"interfaceCode":"","data":{接口报文}}
                            JSONObject obj = JSON.parseObject(strMsg);
                            interfaceCode = obj.getString("interfaceCode");
                            params = obj.getString("data");
                        } else {//解析xml格式  <root><interfaceCode></interfaceCode><data>接口报文</data></root>
                            Map map = XMLUtil.resolveXmlToMapList(strMsg);
                            interfaceCode = String.valueOf(map.get("interfaceCode"));
                            params = XMLUtil.callMapToXML((Map)map.get("data"));
                        }
                        if (null != discoveryClient.getEurekaClient()) {
                            Application app = discoveryClient.getEurekaClient().getApplication("receive-server".toUpperCase());
                            InstanceInfo instances = app.getInstances().get(0);
                            String url = instances.getHomePageUrl();
                            if (url.endsWith("/")) {
                                url += "receive/data/inner/" + interfaceCode + "?traceId="+ traceId+"&useIP="+key;
                            } else {
                                url += "/receive/data/inner/" + interfaceCode + "?traceId="+ traceId+"&useIP="+key;
                            }
                            result = HttpService.sendHttp(url, "post", null, params, format);
                        }

                        LoggerUtil.warnSource("traceId = {}; requestUrl = {}; requestIp = {}; exeSys = gateway; requestTime = "+ DateUtils.Now.fmt_yyyyMMdd_HHmmss()+"; infoType = info; responseBody = {};",traceId,"receive/data/inner/" + interfaceCode + "/",key,result.replaceAll("\n","").replaceAll("\r",""));
                        //返回值长度
                        byte[] dataByteArr = result.getBytes("UTF-8");
                        int dataLen = dataByteArr.length;
                        byte[] dataByteLen = int2BytesLH(dataLen);
                        //回写返加报文
                        OutputStream os = socket.getOutputStream();
                        //发送实际数据长度
                        os.write(dataByteLen, 0, dataByteLen.length);
                        //发送实际数据内容
                        os.write(dataByteArr, 0, dataByteArr.length);
                        log.debug("dataByteArr=" + result);
                        os.flush();
                        // 关闭输出流
                        socket.shutdownOutput();
                        // 关闭资源
                        if(null != bufferedReader) {
                            bufferedReader.close();
                        }
                    }
                } catch (Exception e) {
                    log.error("未能正确执行：" + e.getMessage());
                } finally {
                    IOUtils.closeQuietly(bufferedReader);
                    IOUtils.closeQuietly(socket);
                }
            }

            /**
             * description:  将int转成字节数组
             */
            private byte[] int2BytesLH(int n) {
                byte[] b = new byte[4];
                b[0] = (byte) (n & 0xff);
                b[1] = (byte) (n >> 8 & 0xff);
                b[2] = (byte) (n >> 16 & 0xff);
                b[3] = (byte) (n >> 24 & 0xff);
                return b;
            }

        }
    }

}
